package com.xishu.controller;

import com.xishu.annotation.valid.Admin;
import com.xishu.annotation.valid.MethodValidator;
import com.xishu.aop.Validator;
import com.xishu.bo.UpdatePassword;
import com.xishu.client.EmailClient;
import com.xishu.config.Config;
import com.xishu.config.Constant;
import com.xishu.entity.Company;
import com.xishu.entity.User;
import com.xishu.entity.plat.GlobalSetting;
import com.xishu.entity.plat.PrivilegeMapping;
import com.xishu.entity.plat.UserCompanyMapping;
import com.xishu.entity.plat.UserShopMapping;
import com.xishu.entity.shop.Shop;
import com.xishu.response.ResponseData;
import com.xishu.response.ResponseStatus;
import com.xishu.response.VerifyUtil;
import com.xishu.service.*;
import com.xishu.util.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import net.sf.json.JSONArray;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.xishu.config.Constant.*;
import static com.xishu.response.ResponseStatus.*;

/**
 * 用户管理
 */
@RestController
@Api(description = "用户管理,管理员的，商家的账号均在这里")
public class UserController implements Tools {
    private static Logger logger = LoggerFactory.getLogger(UserController.class);

    private static GlobalService globalService = GlobalService.getInstance();
    private CommonService commonService = CommonService.getInstance();

    @ApiOperation(value = "查询当前用户信息")
    @GetMapping(value = ("/user/userInfo"), produces = ("application/json;charset=UTF-8"))
    public ResponseData userInfo() throws Exception {
        User user = ServletUtil.getUser();
        return commonService.createResponse(user);
    }

    /**
     * 创建
     *
     * @param user
     * @return
     */
    @ApiOperation(value = "创建用户,如果是创建餐厅公司账号，需要绑定公司的ID")
    @PostMapping(value = ("/user/user"), produces = ("application/json;charset=UTF-8"))
    @Admin
    public ResponseData create(@RequestBody User user) throws Exception {
        VerifyUtil.isEmpty(user.getPassword(), user.getName());

        if (user.getCompanyId() != null) {
            user.setCompanyManager(true);
        }

        user.setName(user.getName().toLowerCase());
        UserService.getInstance().createUser(user);
        return commonService.createObject(user, null, true, INVITE_EMAIL_EXIST);
    }

    /**
     * 修改
     *
     * @param user
     * @return
     */
    @ApiOperation(value = "修改用户,admin用户才有权限")
    @PutMapping(value = ("/user/user/modify"), produces = ("application/json;charset=UTF-8"))
    public ResponseData updateUser(@RequestBody User user) throws Exception {
        User currentUser = ServletUtil.getUser();
        VerifyUtil.verify(() -> new Validator().isPlatManager(currentUser), ResponseStatus.PRIVILEGE);
        //平台管理员，不能修改超级管理员
        User userInDb = UserService.getInstance().findUser(user.getId());

        if (!getBoolean(currentUser.getAdmin()) && getBoolean(userInDb.getAdmin())) {
            VerifyUtil.verify(() -> false, ResponseStatus.PRIVILEGE);
        }

        if (isNotEmpty(user.getName())) {
            user.setName(user.getName().toLowerCase());
        }

        UserService.getInstance().resetShopPrivilege(user);

        //权限通过后，可以直接修改了
        ResponseData responseData = UserService.getInstance().updateUser(user);
        //如果还有在线设备的数量，那么修改对应分店的数量
        if (user.getOnlineTouchDeviceNumber() != null) {
            logger.info("will update user {} touch device number {}", user.getId(), user.getOnlineTouchDeviceNumber());
            //至少要比默认的要多
            VerifyUtil.verify(() -> getInt(user.getOnlineTouchDeviceNumber()) >= Config.getInstance().defaultOnlineTouchDeviceNumber());
            //餐厅账号才可以
            if (getBoolean(userInDb.getCompanyManager()) || userInDb.getCompanyId() != null) {
                //查询映射关系
                Long companyId = userInDb.getCompanyId();
                logger.info("the company id is {}", companyId);
                if (companyId != null) {
                    List<Shop> shopList = ShopService.getInstance().findShopListByCompanyId(companyId);
                    for (Shop shop : shopList) {
                        shop.setOnlineTouchDeviceNumber(user.getOnlineTouchDeviceNumber());
                        commonService.save(shop);
                    }
                }
            }
        }

        //如果修改了餐厅用户的权限，那么对应的用户的权限列表也需要跟着更新,在新列表里面的保存，没有在新列表里面的删除
        if (getBoolean(userInDb.getCompanyManager()) && isNotEmpty(user.getPrivilegeList())) {
            List<String> companyUserPrivilegeList = user.getPrivilegeList();
            logger.info("companyUserPrivilegeList is {}", StringUtils.join(companyUserPrivilegeList, ","));

            //查询餐厅账号对应的公司，然后查询公司下面的权限列表
            UserCompanyMapping userCompanyMapping = new UserCompanyMapping();
            userCompanyMapping.setUserId(userInDb.getId());
            Optional<UserCompanyMapping> userCompanyMappingOptional = commonService.searchOneOptional(userCompanyMapping);
            if (userCompanyMappingOptional.isPresent()) {
                UserCompanyMapping userCompanyMappingInDb = userCompanyMappingOptional.get();
                Long companyId = userCompanyMappingInDb.getCompanyId();
                //查询权限列表
                PrivilegeMapping privilegeMapping = new PrivilegeMapping();
                privilegeMapping.setCompanyId(companyId);
                privilegeMapping.setLevel(PRIVILEGE_SHOP_LEVEL);
                List<PrivilegeMapping> privilegeMappingList = commonService.searchAll(privilegeMapping);
                UserService userService = UserService.getInstance();

                //同步默认权限
                for (PrivilegeMapping mapping : privilegeMappingList) {
                    if (getBoolean(mapping.getDefaultPrivilege())) {
                        mapping.setPrivilegeList(userService.addPosPrivilege(companyUserPrivilegeList, mapping.getPrivilegeList()));
                        logger.info("default Privilege update to mapping {}", mapping.getId());
                        commonService.save(mapping);
                        continue;
                    }

                    logger.info("will check mapping {}", mapping.getId());
                    List<String> privilegeList = mapping.getPrivilegeList();
                    logger.info("privilegeList is {}", StringUtils.join(privilegeList, ","));
                    privilegeList = reAssignPrivilege(privilegeList, companyUserPrivilegeList);
                    logger.info("after privilegeList is {}", StringUtils.join(privilegeList, ","));
                    mapping.setPrivilegeList(userService.addPosPrivilege(privilegeList, mapping.getPrivilegeList()));
                    commonService.save(mapping);
                }
            }
        }

        //如果修改了权限，添加修改餐厅权限的时间
        if (isNotEmpty(user.getPrivilegeList())) {
            UserService.getInstance().addUserExpireTime(user.getId());
        }

        return responseData;
    }

    /**
     * 是否是一级权限
     *
     * @param privilege
     * @return
     */
    private boolean isLevelOnePrivilege(String privilege) {
        List<String> strings = Arrays.asList("Feedback", "xt_gl", "sjcdgl");
        Optional<String> stringOptional = strings.stream().filter(p -> equals(p, privilege)).findFirst();

        if (stringOptional.isPresent()) {
            return true;
        }

        String[] levelOnePrivilege = privilege.split("-");
        if (levelOnePrivilege.length == 1) {
            return true;
        }

        return equals(levelOnePrivilege[0], levelOnePrivilege[1]);
    }

    /**
     * 一级权限名称
     *
     * @param privilege
     * @return
     */
    private String getLevelOneName(String privilege) {
        if (equals(privilege, "Feedback")) {
            return "xxfkgl";
        } else if (equals(privilege, "xt_gl")) {
            return "xy";
        } else if (equals(privilege, "sjcdgl")) {
            return "sjcdgl";
        }

        String[] levelOnePrivilege = privilege.split("-");
        if (levelOnePrivilege.length == 2) {
            return levelOnePrivilege[0];
        } else {
            logger.info("privilege {} length invalid", privilege);
            return "";
        }

    }

    /**
     * 重新分配权限
     *
     * @param shopPrivilege
     * @param privilegeList
     * @return
     */
    private List<String> reAssignPrivilege(List<String> shopPrivilege, List<String> privilegeList) {
        if (isEmpty(shopPrivilege)) {
            logger.info("old privilege is empty");
            return privilegeList;
        }

        Set<String> newPrivilegeList = new HashSet<>();
        //先找一级权限
        List<String> levelOneList = shopPrivilege.stream().filter(p -> {
            return isLevelOnePrivilege(p);
        }).collect(Collectors.toList());

        //如果一级权限在新的列表里面找不到了，那么把新的列表全部重新添加到老的权限列表里面了
        for (String privilege : levelOneList) {
            if (privilegeList.contains(privilege)) {
                newPrivilegeList.add(privilege);
                continue;
            }

            //如果不包含了，那么过滤所有的子权限
            List<String> levelTwoList = privilegeList.stream().filter(p -> {
                String levelOneName = getLevelOneName(p);
                return p.contains(levelOneName + "_");
            }).collect(Collectors.toList());

            newPrivilegeList.addAll(levelTwoList);
        }

        //处理二级权限
        List<String> levelTwoList = shopPrivilege.stream().filter(p -> {
            return !isLevelOnePrivilege(p);
        }).collect(Collectors.toList());

        for (String privilege : levelTwoList) {
            if (privilegeList.contains(privilege)) {
                newPrivilegeList.add(privilege);
                continue;
            }

            //如果没有包含，那么看一级目前是否包含了
            List<String> otherLevelOneList = privilegeList.stream().filter(p -> {
                return isLevelOnePrivilege(p);
            }).collect(Collectors.toList());

            for (String p : otherLevelOneList) {
                String topName = getLevelOneName(p);
                if (privilege.contains(topName)) {
                    newPrivilegeList.add(privilege);
                    break;
                }
            }
        }

        return new ArrayList<>(newPrivilegeList);
    }

    @ApiOperation(value = "修改用户自己的头像以及密码")
    @PutMapping(value = ("/user/own/modify"), produces = ("application/json;charset=UTF-8"))
    public ResponseData updateOwner(@RequestBody User reqUser) throws Exception {
        User user = ServletUtil.getUser();

        if (reqUser.getPassword() != null) {
            user.setSalt(Sha256Util.generateSalt());
            user.setPassword(Sha256Util.sha256(reqUser.getPassword(), user.getSalt()));
            commonService.directSave(user);
        }

        if (isNotEmpty(reqUser.getImgId())) {
            user.setImgId(reqUser.getImgId());
            commonService.directSave(user);
        }

        return ResponseData.emptyResponse();
    }

    @ApiOperation(value = "搜索用户,admin用户才有权限")
    @PutMapping(value = ("/user/user"), produces = ("application/json;charset=UTF-8"))
    public ResponseData search(@RequestBody User user) throws Exception {
        return commonService.searchResponse(user, (u) -> {
            if (isNotEmpty(u.getPassword())) {
                u.setPassword(Constant.USER_DEFAULT_PASSWORD);
                u.setConfirmPassword(Constant.USER_DEFAULT_PASSWORD);
            }

            if (isNotEmpty(u.getSecondaryPassword())) {
                u.setSecondaryPassword(Constant.USER_DEFAULT_PASSWORD);
                u.setConfirmSecondaryPassword(Constant.USER_DEFAULT_PASSWORD);
            }

            //经理权限，如果是0，不显示
            if (isNotEmpty(u.getShopPrivilegeList())) {
                List<String> shopPrivilegeList = u.getShopPrivilegeList().stream().filter(p -> !equals("0", p)).collect(Collectors.toList());
                u.setShopPrivilegeList(shopPrivilegeList);
            }

            if (getBoolean(user.getQueryShopName())) {
                List<UserShopMapping> mappingList = MappingService.getInstance().findUserShopMappingByUserId(u.getId());
                if (isNotEmpty(mappingList)) {
                    ShopService shopService = ShopService.getInstance();
                    List<Shop> shopList = shopService.findShopByList(mappingList.stream().map(p -> p.getShopId()).collect(Collectors.toList()));
                    List<String> zhList = shopList.stream().map(p -> shopService.getShopNameZh(p)).collect(Collectors.toList());
                    u.setShopName_zh(com.alibaba.fastjson.JSONArray.toJSON(zhList).toString());

                    List<String> enList = shopList.stream().map(p -> shopService.getShopNameEn(p)).collect(Collectors.toList());
                    u.setShopName_en(com.alibaba.fastjson.JSONArray.toJSON(enList).toString());

                    Shop shop = shopList.get(0);
                    try {
                        Company company = CompanyService.getInstance().findCompanyById(shop.getCompanyId());
                        u.setCompanyName(company.getName());
                    } catch (Exception e) {
                        logger.error("e", e);
                    }
                } else {
                    Long shopId = u.getShopId();
                    if (shopId != null) {
                        ShopService shopService = ShopService.getInstance();
                        try {
                            Shop shop = shopService.findShopById(shopId);
                            List<String> zhList = Arrays.asList(shopService.getShopNameZh(shop));
                            u.setShopName_zh(com.alibaba.fastjson.JSONArray.toJSON(zhList).toString());

                            List<String> enList = Arrays.asList(shopService.getShopNameEn(shop));
                            u.setShopName_en(com.alibaba.fastjson.JSONArray.toJSON(enList).toString());

                            try {
                                Company company = CompanyService.getInstance().findCompanyById(shop.getCompanyId());
                                u.setCompanyName(company.getName());
                            } catch (Exception e) {
                                logger.error("e", e);
                            }

                        } catch (Exception e) {
                            logger.error("e", e);
                        }
                    }
                }
            }
        });
    }

    @ApiOperation(value = "查询餐厅的服务员列表")
    @PutMapping(value = ("/user/shop/waiter"), produces = ("application/json;charset=UTF-8"))
    public ResponseData searchShopWaiter(@RequestBody UserShopMapping userShopMapping) throws Exception {
        //至少要是餐厅经理，并且关联了相关的餐厅
        User user = ServletUtil.getUser();
        Long shopId = userShopMapping.getShopId();
        VerifyUtil.verify(() -> new Validator().isShopManager(user), ResponseStatus.PRIVILEGE);
        VerifyUtil.verify(() -> shopId != null);

        if (!getBoolean(user.getAdmin())) {
            UserShopMapping shopMapping = MappingService.getInstance().findUserShopMapping(user.getId(), shopId);
            VerifyUtil.verify(() -> shopMapping != null, ResponseStatus.PRIVILEGE);
        }

        //查询用户列表
        User searchUser = new User();
        searchUser.setShopId(shopId);
        searchUser.setWaiter(true);
        searchUser.setEntity(true);
        List<User> userList = commonService.searchAll(searchUser);

        userList.forEach(u -> UserService.getInstance().cleanUser(u));
        return commonService.createResponse(userList);
    }

    /**
     * 登录
     *
     * @param user
     * @return
     */
    @PutMapping(value = ("/login"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(mustLogin = false)
    @ApiOperation(value = "登录")
    public ResponseData login(@RequestBody User user) throws Exception {
        //先做下兼容性处理
        if (isNotEmpty(user.getToken())) {
            RecaptchaService.getInstance().check(user.getToken(), 0.5);
        }

        boolean bEndLogin = getBoolean(user.getbEndLogin());

        User loginUser = UserService.getInstance().login(user);

        ServletUtil.getSession().invalidate();

        if (loginUser == null) {
            UserService.getInstance().addUserLoginFailedTime(user.getName());
            Thread.sleep(300);
            VerifyUtil.throwError(ResponseStatus.USER_NAME_OR_PASSWORD_WRONG);
        }

        VerifyUtil.verify(() -> getInt(loginUser.getStatus()) == Constant.USER_STATUS_OK, ResponseStatus.USER_LOCKED);

        if (bEndLogin) {
            VerifyUtil.verify(() -> getInt(loginUser.getFailedTime()) < Config.getInstance().passwordMaxWrongNumber(), PASSWORD_WRONG_REACH_MAX_TIME_BEND);
        } else {
            VerifyUtil.verify(() -> getInt(loginUser.getFailedTime()) < Config.getInstance().passwordMaxWrongNumber(), PASSWORD_WRONG_REACH_MAX_TIME);
        }


        ServletUtil.setUserId(loginUser);
        String uuid = UUID.randomUUID().toString();
        JRedisUtil.setKV(uuid, loginUser.getId());
        ServletUtil.getResponse().setHeader(Constant.SESSION_TOKEN, uuid);

        //如果是商家账号，经理，则需要判断对应的公司是否已经开通
        if (getBoolean(loginUser.getCompanyManager())) {
            //公司查询映射关系
            UserCompanyMapping userCompanyMapping = new UserCompanyMapping();
            userCompanyMapping.setEntity(true);
            userCompanyMapping.setUserId(loginUser.getId());
            List<UserCompanyMapping> mappingList = commonService.searchAll(userCompanyMapping);

            boolean allMatch = mappingList.stream().allMatch(mapping -> {
                logger.info("mapping id {}, company id {}", mapping.getId(), mapping.getCompanyId());
                Long companyId = mapping.getCompanyId();
                try {
                    Company company = CompanyService.getInstance().findCompanyById(companyId);
                    return company.getPass() == null || getBoolean(company.getPass());
                } catch (Exception e) {
                    return false;
                }
            });

            VerifyUtil.verify(() -> allMatch, ResponseStatus.USER_DEACTIVED);
        } else if (getBoolean(loginUser.getShopManager())) {
            //通过映射读取所属分店，再通过分店读取所属的公司
            UserShopMapping userShopMapping = new UserShopMapping();
            userShopMapping.setEntity(true);
            userShopMapping.setUserId(loginUser.getId());
            Optional<UserShopMapping> userShopMappingOptional = commonService.searchOneOptional(userShopMapping);

            //分店已经不存在了
            VerifyUtil.verify(() -> userShopMappingOptional.isPresent(), ResponseStatus.USER_DEACTIVED);
            Long shopId = userShopMappingOptional.get().getShopId();
            Optional<Shop> shopOptional = ShopService.getInstance().findShopOptional(shopId);
            VerifyUtil.verify(() -> shopOptional.isPresent(), ResponseStatus.USER_DEACTIVED);
            //分店直接直接company_id
            Long companyId = shopOptional.get().getCompanyId();
            Company company = CompanyService.getInstance().findCompanyById(companyId);
            VerifyUtil.verify(() -> company.getPass() == null || getBoolean(company.getPass()), ResponseStatus.USER_DEACTIVED);
        }

        //如果不是超管，也不是管理员，添加检查时间标记
        if (getBoolean(loginUser.getCompanyManager()) || getBoolean(loginUser.getShopManager())) {
            ServletUtil.setSessionAttribute(USER_SESSION_LOGIN_TIME, System.currentTimeMillis());
            Long companyId = UserService.getInstance().findCompanyIdByUserId(loginUser.getId());
            if (companyId != null) {
                ServletUtil.setSessionAttribute(SESSION_COMPANY_ID, companyId);
            } else {
                logger.info("can not find the company id");
            }
        }

        //清空登录失败次数
        loginUser.setFailedTime(0);
        commonService.save(loginUser);

        //如果是B端登录的账号，那么添加标记，在修改权限的时候，B端登录的账号不用重新登录
        if (bEndLogin) {
            ServletUtil.setSessionAttribute(SESSION_B_END_LOGIN, true);
        }

        return commonService.createResponse(loginUser);
    }

    @PutMapping(value = ("/logout"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(mustLogin = false)
    @ApiOperation(value = "注销")
    public ResponseData logout() throws Exception {
        Long userId = ServletUtil.getUserId();

        if (userId == null) {
            ServletUtil.cleanSessionAttribute(Constant.SESSION_USER_ID);
            ResponseData.emptyResponse();
        }

        User reqUser = new User();
        reqUser.setId(userId);
        Optional<User> userOptional = commonService.searchOneOptional(reqUser);
        if (!userOptional.isPresent()) {
            ServletUtil.cleanSessionAttribute(Constant.SESSION_USER_ID);
            ResponseData.emptyResponse();
        }

        if (reqUser != null) {
            String headerToken = ServletUtil.getHeaderToken();

            //清除缓存
            if (headerToken != null) {
                JRedisUtil.delKV(headerToken);
            }

            ServletUtil.cleanSessionAttribute(Constant.SESSION_USER_ID);
        }

        return ResponseData.emptyResponse();
    }

    @MethodValidator(mustLogin = false)
    @ApiOperation(value = "点击忘记密码邮件，然后在会话里面打标记")
    @GetMapping(value = ("/confirm/forgot/password/{code}"), produces = ("application/json;charset=UTF-8"))
    public ResponseData confirmForgotPassword(@PathVariable("code") String code) throws Exception {
        String expireTime = JRedisUtil.getKV(String.format("%s.expire.time", code));
        VerifyUtil.verify(() -> isNotEmpty(expireTime), ResponseStatus.EMAIL_EXPIRE);

        //当前时间不能超过过期时间
        VerifyUtil.verify(() -> System.currentTimeMillis() < Long.valueOf(expireTime), ResponseStatus.EMAIL_EXPIRE);

        String emailAndTime = AES.decrypt(code);
        VerifyUtil.verify(() -> isNotEmpty(emailAndTime), ResponseStatus.EMAIL_EXPIRE);

        String email = emailAndTime.split(":")[0];
        String time = emailAndTime.split(":")[1];

        //当前的CODE必须要和最后一个CODE相同
        String emailCode = JRedisUtil.getKV(String.format("%s.forgot.password", email));
        VerifyUtil.verify(() -> isNotEmpty(emailCode), ResponseStatus.EMAIL_EXPIRE);
        VerifyUtil.verify(() -> equals(emailCode, code), ResponseStatus.EMAIL_EXPIRE);

        //打个标记，后面修改密码的时候，直接查询出来这个邮件，然后去做修改
        ServletUtil.setSessionAttribute(Constant.RESET_PASSWORD_EMAIL, email);
        return ResponseData.emptyResponse();
    }

    @MethodValidator(mustLogin = false)
    @ApiOperation(value = "发送忘记密码邮件")
    @PutMapping(value = ("/forgot/password"), produces = ("application/json;charset=UTF-8"))
    public ResponseData forgotPassword(@RequestBody User user) throws Exception {
        GlobalSetting globalSetting = globalService.getGlobalSetting();
        String address = globalSetting.getAddress();
        String city = globalSetting.getCity();
        String companyWebsite = globalSetting.getCompanyWebsite();
        String emailAddress = globalSetting.getEmailAddress();
        String email = user.getEmail();
        VerifyUtil.isEmpty(email);
        User userByEmail = UserService.getInstance().findUserByEmailOrName(email);

        VerifyUtil.verify(() -> userByEmail != null, ResponseStatus.EMAIL_NOT_EXIST);

        String code = AES.encrypt(String.format("%s:%s", email, System.currentTimeMillis()));
        //添加过期时间30分钟
        JRedisUtil.setKV(String.format("%s.expire.time", code), System.currentTimeMillis() + DateUtil.ONE_MINUTE * 30);
        //添加邮箱忘记密码时间
        JRedisUtil.setKV(String.format("%s.forgot.password", email), code);

        String validAddress = Config.getInstance().getEndPoint() + "/confirm/forgot/password/" + code;
        logger.info("valid address is {} and user id is {}", validAddress, userByEmail.getId());

        String title = "[DoLA] Reset Password. 重設密碼。";
        String userCompanyName = UserService.getInstance().getUserCompanyName(userByEmail);
        String logoUrl = PlatService.getInstance().getLogoUrl();
        logger.info("logo url is {}", logoUrl);

        String content = String.format("<!DOCTYPE html>\n" +
                "<html lang=\"en\">\n" +
                "<head>\n" +
                "    <meta charset=\"UTF-8\">\n" +
                "    <title>Title</title>\n" +
                "</head>\n" +
                "<body>\n" +
                "<div style=\"padding-right: 10%%\" align=\"right\"><img style=\"width: 100px;height: 100px;\" src=\"%s\"></div>\n" +
                "<div style=\"padding-left: 10%%\">\n" +
                "    <div>Hi %s,</div>\n" +
                "    <br/>\n" +
                "    <div>To reset your password, please click on the link below:</div>\n" +
                "    <br>\n" +
                "\n" +
                "    <div>您好 %s，</div>\n" +
                "    <div>您提交了重設密碼的申請。爲了完成您的操作，请點擊以下地址：</div>\n" +
                "    <br/>\n" +
                "    %s\n" +
                "    <br/>\n" +
                "    <br/>\n" +
                "    <div>If you did not initiate this request, simply ignore this email.如果您本人未進行密碼重設，請忽略這封電郵。</div>\n" +
                "</div>\n" +
                "\n" +
                "<br/>\n" +
                "<br/>\n" +
                "\n" +
                "<div align=\"center\">\n" +
                "    <div style=\"color: #808080\">" + address + "<br/>\n" +
                "        " + city + "<br/>\n" +
                "    </div>\n" +
                "    <div style=\"color: #4793A3;text-decoration: none !important;\">" + emailAddress + " | " + companyWebsite + "</div>\n" +
                "</div>\n" +
                "</body>\n" +
                "</html>", logoUrl, userCompanyName, userCompanyName, Config.getInstance().getEndPoint() + "/manage/index.html#/czmm?code=" + code);

        EmailClient.getInstance().sendEmail(email, title, content);

        return ResponseData.emptyResponse();
    }

    @PutMapping(value = ("/reset/password"), produces = ("application/json;charset=UTF-8"))
    @ApiOperation(value = "重置密码，商家注册的时候重置密码也是它,只用发送密码")
    @MethodValidator(mustLogin = false)
    public ResponseData resetPassword(@RequestBody User reqUser) throws Exception {
        Object email = ServletUtil.getSessionAttribute(Constant.RESET_PASSWORD_EMAIL);
        VerifyUtil.verify(() -> email != null, ResponseStatus.RANDOM_CODE_EXPIRE);

        User user = UserService.getInstance().findUserByEmail(email.toString());
        VerifyUtil.verify(() -> user != null, ResponseStatus.USER_NOT_EXIST);

        //找到了，需要重新设置下
        String salt = Sha256Util.generateSalt();
        String newPassword = Sha256Util.sha256(reqUser.getPassword(), salt);
        user.setPassword(newPassword);
        user.setSalt(salt);

        if (isNotEmpty(reqUser.getNickName())) {
            user.setNickName(reqUser.getNickName());
        }

        //如果有添加二级密码，重新设置下
        if (isNotEmpty(reqUser.getSecondaryPassword())) {
            String secondarySalt = Sha256Util.generateSalt();
            String newSecondaryPassword = Sha256Util.sha256(reqUser.getSecondaryPassword(), secondarySalt);

            user.setSecondarySalt(secondarySalt);
            user.setSecondaryPassword(newSecondaryPassword);
        }

        //将失败次数清空
        user.setFailedTime(0);
        CommonService.getInstance().save(user);
        return commonService.createResponse(user);
    }

    @ApiOperation(value = "商家管理员创建商家经理账号，或者是服务员账号")
    @PutMapping(value = ("/user/shop/user"), produces = ("application/json;charset=UTF-8"))
    public ResponseData createShopUser(@RequestBody User user) throws Exception {
        Long shopId = user.getShopId();
        Long companyId = user.getCompanyId();
        user.setAdmin(false);
        user.setPlatManager(false);
        VerifyUtil.isEmpty(user.getName());
        user.setName(user.getName().toLowerCase());

        if (shopId != null) {
            Shop shop = ShopService.getInstance().findShopById(shopId);
            user.setCompanyId(shop.getCompanyId());
            companyId = shop.getCompanyId();
        } else if (companyId != null) {
            Company company = commonService.findEntity(companyId, Company.class);
            user.setCompanyId(company.getId());
        }

        Long checkCompanyId = companyId;

        VerifyUtil.verify(() -> new Validator().companyRelated(checkCompanyId), ResponseStatus.PRIVILEGE);

        //如果当前不是超级管理员，那么校验是否满足能创建分店的最大值
        User userInDb = ServletUtil.getUser();
        if (!getBoolean(userInDb.getAdmin())) {
            Company company = CompanyService.getInstance().findCompanyById(companyId);
            List<User> userList = UserService.getInstance().findUserListByCompnayId(companyId);
            VerifyUtil.verify(() -> userList.size() + 1 <= getInt(company.getUserNumbers()), ResponseStatus.REACH_MAX_ACCOUNT_NUMBER);
        }

        String salt = Sha256Util.generateSalt();

        if (isNotEmpty(user.getPassword())) {
            user.setSalt(salt);
            user.setPassword(Sha256Util.sha256(user.getPassword(), salt));
        }

        //服务员可以没有二级密码
        if (isNotEmpty(user.getSecondaryPassword())) {
            VerifyUtil.verify(() -> Pattern.matches("\\d{6}", user.getSecondaryPassword()), ResponseStatus.SIX_NUMBER);
            String secondarySalt = Sha256Util.generateSalt();
            user.setSecondarySalt(secondarySalt);
            user.setSecondaryPassword(Sha256Util.sha256(user.getSecondaryPassword(), secondarySalt));
        }

        return commonService.createObject(user, (u) -> {
            if (shopId != null) {
                try {
                    Shop shop = ShopService.getInstance().findShopById(shopId);
                    //添加用户与餐厅的映射关系
                    UserShopMapping userShopMapping = new UserShopMapping();
                    userShopMapping.setUserId(u.getId());
                    userShopMapping.setUserName(u.getName());
                    userShopMapping.setShopId(shopId);

                    userShopMapping.setShopName(shop.getName());
                    commonService.createObject(userShopMapping);
                } catch (Exception e) {
                    logger.error("e", e);
                }
            } else if (isNotEmpty(u.getShopIdString())) {
                String shopIdString = u.getShopIdString();
                JSONArray jsonArray = JSONArray.fromObject(shopIdString);
                logger.info("json array is {]", jsonArray.toString());
                for (Object o : jsonArray) {
                    JSONArray shopIdList = (JSONArray) o;
                    Long oneShopId = Long.valueOf(String.valueOf(shopIdList.get(0)));

                    try {
                        Shop shop = ShopService.getInstance().findShopById(oneShopId);
                        //添加用户与餐厅的映射关系
                        UserShopMapping userShopMapping = new UserShopMapping();
                        userShopMapping.setUserId(u.getId());
                        userShopMapping.setUserName(u.getName());
                        userShopMapping.setShopId(oneShopId);
                        userShopMapping.setShopName(shop.getName());
                        commonService.createObject(userShopMapping);
                    } catch (Exception e) {
                        logger.error("e", e);
                    }
                }
            }
        }, true, INVITE_EMAIL_EXIST);
    }

    @ApiOperation(value = "商家管理员修改商家经理账号，或者是服务员账号")
    @PutMapping(value = ("/user/shop/user/modify"), produces = ("application/json;charset=UTF-8"))
    public ResponseData modifyShopUser(@RequestBody User reqUser) throws Exception {
        //当前用户一定要是餐厅账号
        User currentUser = ServletUtil.getUser();
        VerifyUtil.verify(() -> new Validator().isShopManager(currentUser));

        //清空名称，以避免校验出问题
        reqUser.setName(null);

        User userInDb = commonService.findEntity(reqUser);
        //修改的一定只能是经理以及服务员
        VerifyUtil.verify(() -> getBoolean(userInDb.getCompanyManager()) || getBoolean(userInDb.getShopManager()) || getBoolean(userInDb.getWaiter()));
        //当前账号一定要具有该分店的权限
        reqUser.setAdmin(null);
        reqUser.setPlatManager(null);
        UserService.getInstance().resetShopPrivilege(reqUser);

        if (isNotEmpty(reqUser.getName())) {
            reqUser.setName(reqUser.getName().toLowerCase());
        }

        //如果是服务员，那么name字段不能够修改
        if (getBoolean(userInDb.getWaiter())) {
            reqUser.setName(null);
            reqUser.setPassword(null);
            reqUser.setSecondaryPassword(null);
            reqUser.setShopManager(false);
            reqUser.setAdmin(false);
            reqUser.setPlatManager(false);
        }

        //如果是将某一个账号设置成店铺经理，刚好这个账号还是餐厅账号，那么添加该账号的companyId
        if (getBoolean(reqUser.getShopManager()) && getBoolean(userInDb.getCompanyManager())) {
            Long companyId = UserService.getInstance().findCompanyIdByUserId(userInDb.getId());
            reqUser.setCompanyId(companyId);

            //更新shopId以及shopId String
            //查询分店
            List<Shop> shopList = ShopService.getInstance().findShopListByCompanyId(companyId);
            if (isNotEmpty(shopList)) {
                Shop shop = shopList.get(0);
                reqUser.setShopId(shop.getId());
                List<Long> shopIdList = new ArrayList<>();
                shopIdList.add(shop.getId());
                List<List<Long>> shopIdStringList = new ArrayList<>();
                shopIdStringList.add(shopIdList);

                reqUser.setShopIdString(JSONArray.fromObject(shopIdStringList).toString());
            }

            //给这个,查询默认权限是否存在，如果存在，则用默认名称
            Optional<PrivilegeMapping> defaultMappingPrivilege = UserService.getInstance().findDefaultMappingPrivilege(companyId);
            if (!defaultMappingPrivilege.isPresent()) {
                //没有默认权限，那么新建一个默认权限
                PrivilegeMapping privilegeMapping = new PrivilegeMapping();
                privilegeMapping.setDefaultPrivilege(true);
                privilegeMapping.setName("Default Permission");
                privilegeMapping.setLevel(PRIVILEGE_SHOP_LEVEL);
                privilegeMapping.setCompanyId(companyId);
                //将用户的权限写到分店权限里面去
                List<String> userInDbPrivilegeList = userInDb.getPrivilegeList();

                //默认权限包含店铺状态管理
                if (!userInDbPrivilegeList.contains("sjzhgl_bdckdpzt")) {
                    userInDbPrivilegeList.add("sjzhgl_bdckdpzt");
                }

                privilegeMapping.setPrivilegeList(userInDbPrivilegeList);
                commonService.createObject(privilegeMapping);

                //再把创建好的权限，写到用户的privilegeList里面
                List<String> shopPrivilegeList = new ArrayList<>();
                shopPrivilegeList.add(privilegeMapping.getId() + "");
                logger.info("set user privilege list is {}", StringUtils.join(shopPrivilegeList, ","));
                reqUser.setShopPrivilegeList(shopPrivilegeList);
            }

        }

        //如果要修改密码，要做身份校验
        if (isNotEmpty(reqUser.getPassword()) || isNotEmpty(reqUser.getSecondaryPassword())) {
            Boolean canUpdatePassword = new Validator().isPlatManager(currentUser) || getBoolean(currentUser.getCompanyManager());
            VerifyUtil.verify(() -> canUpdatePassword, ResponseStatus.PRIVILEGE);
        }


        ResponseData responseData = UserService.getInstance().updateUser(reqUser);

        return responseData;
    }

    /**
     * 判断二级密码是否正确
     *
     * @param reqUser
     * @return
     */
    @ApiOperation(value = "校验二级密码是否正确，只用输入二级密码")
    @PutMapping(value = ("/user/shop/user/secondaryPassword/check"), produces = ("application/json;charset=UTF-8"))
    public ResponseData secondaryPasswordOk(@RequestBody User reqUser) throws Exception {
        User user = ServletUtil.getUser();
        String secondaryPassword = reqUser.getSecondaryPassword();
        VerifyUtil.verify(() -> isNotEmpty(secondaryPassword));
        String comparePassword = Sha256Util.sha256(secondaryPassword, user.getSecondarySalt());
        VerifyUtil.verify(() -> equals(comparePassword, user.getSecondaryPassword()), ResponseStatus.PASSWORD_WRONG);
        return ResponseData.emptyResponse();
    }

    @ApiOperation(value = "删除用户,admin用户不能被删除")
    @DeleteMapping(value = ("/user/user"), produces = ("application/json;charset=UTF-8"))
    public ResponseData delete(@RequestBody User reqUser) throws Exception {
        UserService userService = UserService.getInstance();
        User user = userService.findUser(reqUser.getId());

        if (equals(user.getName(), "admin")) {
            VerifyUtil.throwError(ResponseStatus.PRIVILEGE);
        }

        //如果自己是商家账号，只能删除商家账号或者经理
        User currentUser = ServletUtil.getUser();
        if (getBoolean(currentUser.getCompanyManager()) || getBoolean(currentUser.getShopManager())) {
            //这里校验是不是在删除平台管理员或者超管
            VerifyUtil.verify(() -> !(getBoolean(user.getAdmin()) || getBoolean(user.getPlatManager())), ResponseStatus.PRIVILEGE);
        }

        //自己不能删除自己
        if (equals(currentUser.getId(), user.getId())) {
            VerifyUtil.throwError(ResponseStatus.PRIVILEGE);
        }

        ResponseData responseData = commonService.delete(reqUser);
        //删除映射关系
        MappingService.getInstance().deleteUserShopMapping(reqUser.getId());
        MappingService.getInstance().deleteUserCompanyMapping(reqUser.getId());

        return responseData;
    }

    @ApiOperation(value = "查询餐厅公司对应的账号")
    @PutMapping(value = ("/company/user"), produces = ("application/json;charset=UTF-8"))
    public ResponseData queryCompanyUser(@RequestBody Company company) throws Exception {
        Long companyId = null;
        if (company.getId() != null) {
            companyId = company.getId();
        }

        if (company.getCompanyId() != null) {
            companyId = company.getCompanyId();
        }

        UserCompanyMapping userCompanyMapping = new UserCompanyMapping();
        userCompanyMapping.setCompanyId(companyId);

        List<UserCompanyMapping> userCompanyMappingList = commonService.searchAll(userCompanyMapping);

        if (isEmpty(userCompanyMappingList)) {
            return ResponseData.emptyResponse();
        }

        User user = new User();
        List<Long> userIds = userCompanyMappingList.stream().map(p -> p.getUserId()).collect(Collectors.toList());
        user.setIds(userIds);
        List<User> users = commonService.searchAll(user);

        Optional<User> userOption = users.stream().filter(p -> getBoolean(p.getCompanyManager())).findFirst();
        if (userOption.isPresent()) {
            User resultUser = userOption.get();
            UserService.getInstance().cleanUser(resultUser);
            return commonService.createResponse(resultUser);
        } else {
            return ResponseData.emptyResponse();
        }
    }

    @ApiOperation(value = "查询分店对应的主账号")
    @PutMapping(value = ("/shop/user"), produces = ("application/json;charset=UTF-8"))
    public ResponseData queryShopUser(@RequestBody Shop shop) throws Exception {
        Long shopId = shop.getId();

        if (shopId == null) {
            shopId = shop.getShopId();
        }

        Shop shopInDb = ShopService.getInstance().findShopById(shopId);
        Company company = CompanyService.getInstance().findCompanyById(shopInDb.getCompanyId());
        return queryCompanyUser(company);
    }

    @ApiOperation(value = "邀请店铺经理")
    @PostMapping(value = ("/manager/user"), produces = ("application/json;charset=UTF-8"))
    public ResponseData inviteManager(@RequestBody User user) throws Exception {
        String email = user.getEmail();
        List<Long> shopIdList = user.getShopIdList();
        List<String> shopPrivilegeList = user.getShopPrivilegeList();

        VerifyUtil.verify(() -> isNotEmpty(email));
        VerifyUtil.verify(() -> isNotEmpty(shopPrivilegeList));
        VerifyUtil.verify(() -> isNotEmpty(shopIdList));

        //判断账号是否已经存在过了
        boolean userExist = UserService.getInstance().isUserExist(user.getEmail());
        VerifyUtil.verify(() -> !userExist, INVITE_EMAIL_EXIST);

        Long shopId = shopIdList.get(0);
        Company company = CompanyService.getInstance().findCompanyByShopId(shopId);
        String lang = company.getLang();

        UserService.getInstance().inviteManager(email, shopIdList, shopPrivilegeList, lang);
        return ResponseData.emptyResponse();
    }

    @ApiOperation(value = "经理注册")
    @GetMapping(value = ("/invite/manager/{code}"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(mustLogin = false)
    public ResponseData managerRegister(@PathVariable("code") String code) throws Exception {
        String emailCode = AES.decrypt(code);
        VerifyUtil.verify(() -> isNotEmpty(emailCode), ResponseStatus.EMAIL_EXPIRE);
        String inviteInfo = JRedisUtil.getKV(emailCode);
        VerifyUtil.verify(() -> isNotEmpty(inviteInfo), ResponseStatus.EMAIL_EXPIRE);

        logger.info("invite info is {}", inviteInfo);
        String[] split = inviteInfo.split("#");
        String email = split[0];

        String currentCode = JRedisUtil.getKV(String.format("%s.current.invite.code", email));
        //校验当前的CODE是否是最新的
        VerifyUtil.verify(() -> equals(currentCode, emailCode), ResponseStatus.EMAIL_EXPIRE);

        //一次只能邀请一个账号
        String shopIdListString = split[1];
        List<Long> shopIdList = Arrays.stream(shopIdListString.split(",")).map(p -> Long.valueOf(p)).collect(Collectors.toList());
        String privilegeListString = split[2];
        String[] shopPrivilegeList = privilegeListString.split("\\|");

        //会话标记有效
        ServletUtil.setSessionAttribute(Constant.RESET_PASSWORD_EMAIL, email);

        //创建账号,查询账号，如果不存在，则创建账号
        User userInDb = UserService.getInstance().findUserByEmail(email);

        if (userInDb != null) {
            UserService.getInstance().cleanUser(userInDb);
            return commonService.createResponse(userInDb);
        } else {
            //创建
            logger.info("will create new user {}", email);
            User user = new User();
            user.setEmail(email);
            user.setName(email);
            user.setShopManager(true);
            user.setShopPrivilegeList(Arrays.asList(shopPrivilegeList));
            user.setShopIdString(shopIdListString);
            user.setShopId(Long.valueOf(shopIdListString));
            commonService.createObject(user);

            //创建账号与商店的映射关系
            for (Long shopId : shopIdList) {
                UserShopMapping userShopMapping = new UserShopMapping();
                userShopMapping.setUserId(user.getId());
                userShopMapping.setUserName(email);
                userShopMapping.setShopId(shopId);
                Shop shop = ShopService.getInstance().findShopById(Long.valueOf(shopIdListString));
                //所有分店的的归属的都是同一个公司
                user.setCompanyId(shop.getCompanyId());
                userShopMapping.setShopName(shop.getName());
                commonService.createObject(userShopMapping);
            }

            //清空返回数据
            UserService.getInstance().cleanUser(user);
            return commonService.createResponse(user);
        }
    }

    @ApiOperation(value = "设置语言,zh表示中文，en表示英文")
    @GetMapping(value = ("/lang/{lang}"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(mustLogin = false)
    public ResponseData setLang(@PathVariable("lang") String lang) {
        ServletUtil.setSessionAttribute("lang", lang);
        return ResponseData.emptyResponse();
    }

    /**
     * 更新密码
     *
     * @param updatePassword
     * @return
     */
    @ApiOperation(value = "更新密码")
    @PutMapping(value = ("/user/update/password"), produces = ("application/json;charset=UTF-8"))
    public ResponseData updatePassword(@RequestBody UpdatePassword updatePassword) throws Exception {
        User user = ServletUtil.getUser();

        String oldPassword = updatePassword.getOldPassword();
        String newPassword = updatePassword.getNewPassword();
        VerifyUtil.verify(() -> isNotEmpty(oldPassword));
        VerifyUtil.verify(() -> isNotEmpty(newPassword));

        //校验旧密码
        Boolean passwordOk = UserService.getInstance().isPasswordOk(user, oldPassword);

        //原始密码不对,添加失败次数
        if (!passwordOk) {
            UserService.getInstance().addUserLoginFailedTime(user.getName());
        }

        VerifyUtil.verify(() -> passwordOk, ResponseStatus.PASSWORD_NOT_RIGHT);

        //修改密码
        String salt = Sha256Util.generateSalt();
        user.setSalt(salt);
        user.setPassword(Sha256Util.sha256(newPassword, salt));
        commonService.save(user);
        return ResponseData.emptyResponse();
    }

    /**
     * 给账号解锁
     *
     * @param reqUser
     * @return
     */
    @ApiOperation(value = "解锁用户")
    @PutMapping(value = ("/unlock/user"), produces = ("application/json;charset=UTF-8"))
    public ResponseData unLockUser(@RequestBody User reqUser) throws Exception {
        //具有平台管理员权限才能修改
        User optUser = ServletUtil.getUser();

        boolean platManager = new Validator().isPlatManager(optUser);
        VerifyUtil.verify(() -> platManager || getBoolean(optUser.getCompanyManager()), ResponseStatus.PRIVILEGE);

        User userInDb = UserService.getInstance().findUser(reqUser.getId());
        //同公司下才能修改
        if (!platManager) {
            boolean canOpt = equals(userInDb.getCompanyId(), optUser.getCompanyId());
            VerifyUtil.verify(() -> canOpt, ResponseStatus.PRIVILEGE);
        }

        //都校验通过了才能解锁
        logger.info("unlock user {} by {}", reqUser.getId(), ServletUtil.getUserId());
        userInDb.setStatus(0);
        userInDb.setFailedTime(0);
        commonService.save(userInDb);

        return ResponseData.emptyResponse();
    }


    /**
     * 更新密码
     *
     * @param updatePassword
     * @return
     */
    @ApiOperation(value = "更新二级密码")
    @PutMapping(value = "/user/update/secondary/password", produces = ("application/json;charset=UTF-8"))
    public ResponseData updateSecondaryPassword(@RequestBody UpdatePassword updatePassword) throws Exception {
        User user = ServletUtil.getUser();

        String oldPassword = updatePassword.getOldPassword();
        String newPassword = updatePassword.getNewPassword();
        VerifyUtil.verify(() -> isNotEmpty(oldPassword));
        VerifyUtil.verify(() -> isNotEmpty(newPassword));

        //校验旧密码
        Boolean passwordOk = UserService.getInstance().isSecondaryPasswordOk(user, oldPassword);

        VerifyUtil.verify(() -> passwordOk, ResponseStatus.PASSWORD_NOT_RIGHT);

        //修改密码
        String salt = Sha256Util.generateSalt();
        user.setSecondarySalt(salt);
        user.setSecondaryPassword(Sha256Util.sha256(newPassword, salt));
        commonService.save(user);
        return ResponseData.emptyResponse();
    }

}
